9
תגובות

Late Binding, Dynamic Dispatch...

פתח OrelBeY ,
איך שלא קוראים לזה. הייתי בטוח שזה יקרה כאן:

public class Main {
  public static void main(String args[])
  {
    (new A()).foo(); // foo from A
    (new A()).bar(); // foo from A
    (new B()).foo(); // foo from B
    (new B()).bar(); // foo from A      <-  ???
  }
}

class A
{
  public void foo() {
      System.out.println("foo from A");
  }
 
  public void bar() {
      foo();
  }
}

class B extends A
{
  public void foo() {
      System.out.println("foo from B");
  }
 
  public void bar() {
      super.foo();
  }
}


למה ככה?

9 תשובות

avatar ענה Michael ב 01 למאי 2014 #

לא הבנתי למה הסימני שאלה שלך. עשית בb.foo קריאה למטודה בparent, עם super. אם תוריד את הsuper. ובעצם תשאיר רק foo(); זה יקרא לb.foo במקום לa.foo
אם תעיף אגב את הדריסה שלך לbar בb, אז b.bar() גם כן ידפיס לך foo from b כמו שאתה רוצה (?).

בכל מקרה,
https://ideone.com/6AlprM

avatar ענה OrelBeY ב 01 למאי 2014 #

אני יודע שאם אני אוריד את ה-super זה יקרא ל-B.foo.

בדיוק הנקודה שלי. זו ההתנהגות שציפיתי אליה - תמיד קריאה למימוש האחרון, לא? כמו עם המילה השמורה static ב-PHP, וכמו שקורה באופן כללי עם פולימורפיזם.

אגב, למה מה שעשית לא עובד עם פעולות סטטיות?

avatar ענה Michael ב 01 למאי 2014 #

super קורא למימוש באב, אז כן זה הגיוני למה זה כתב לך foo from A.
וכי אתה לא יכול לדרוס מטודות סטטיות

avatar ענה OrelBeY ב 02 למאי 2014 #

מעניין. כלומר כשקוראים למתודה שנדרסה, ובעצם מציינים בפירוש לעלות למעלה - גם המימוש עצמו וגם ה-binding עולה למעלה. לאחרון לא ציפיתי, אבל כשחושבים על זה זה דווקא נשמע הגיוני. (לפחות במובן מסוים.)

אני לא קונה את הסיבות שם להיעדרות האפשרות לדרוס מתודות סטטיות. (זה שמדובר במחלקות ולא באובייקטים לא אומר שזה לא הגיוני לדרוס מתודה של מחלקה. זה נשמע מגוחך.) בדקתי בתיעוד הרשמי, וגם שם לא ראיתי הסבר למה זה ככה. יש לך רעיון?

תודה!

avatar ענה intval ב 02 למאי 2014 #

בגלל שזה חסר משמעוט מבחינה רעיונית.
הרעיון של דריסה מיועד לפולימורפיזם
כאשר ניתן להפעיל מטודה על מופע מחלקה בלי לדעת את הסוג הספציפי שלה

for (Iface item : someList) {
    item.ifaceMethod();
}


כשאתה מפעיל מטודה סטטית - אתה לא מפעיל אותה על מופע מסוים, אלא על טיפוס מסוים, וכשאתה עושה את זה - אתה יודע בוודאות את סוג הטיפוס בזמן כתיבת הקוד.
for (Iface item : someList) {
    TypeX.staticM(item);
}

avatar ענה OrelBeY ב 02 למאי 2014 #

@intval לא בהכרח, אפשר להפעיל על משתנה מסוים (בג'אווה, לפחות). מכיוון שאני אשתמש בזה הרבה פחות (ספק אם בכלל), כי אני נמנע מסטטיות באופן כללי, זה לא כזה חשוב לי, אבל זה כן לא מובן לי.

בנוגע לשאלה המקורית (בערך) - אם אני רוצה כן לדרוס מתודה, ואז לקרוא לה דרך super ולשמור על Binding לאובייקט של מחלקת הבן - אני יכול לעשות את זה? כרגע אני לא מצליח למצוא דוגמה מציאותית לזה, אבל במערכות מורכבות אני בטוח שיש לזה שימושים. (פתרון לזה יכול להיות לשנות את מחלקת האב כדי שתאפשר את זה, אבל לא תמיד זה אפשרי. פתרון אחר הוא להשתמש באותו הקוד של מחלקת האב למתודה הזאת ולהוסיף על זה, אבל זה שכפול קוד.)

avatar ענה Michael ב 02 למאי 2014 #

אתה מתכוון למשהו כזה בעצם?

class A {
public void hello() {
 System.out.print("Hello");
}

public void world() {
hello();
}
}

class B extends A {
public void hello() {
super.hello();
System.out.println(" World");
}
}

///
B b = new B();
b.world(); // Hello World

avatar ענה OrelBeY ב 02 למאי 2014 #

זה בעצם מה שהראית לי בהתחלה. התכוונתי למשהו אחר, אבל בתשובה עם דוגמה שכמעט גמרתי לכתוב לך עכשיו ואז מחקתי, קלטתי שבעיות כאלה אפשר לפתור עם הפרדה פשוטה של מתודות. מובן שאם מדובר במערכת שאי אפשר יהיה לשנות את מחלקת האב זה יהיה קצת בעייתי, אבל זה כבר בעיה של גמישות (וצריך לנסות לעבוד על זה מראש כמה שאפשר), וזה כל מקרה בפני עצמו.

תודה רבה לכם שסבלתם את החפירות שלי. :-)

avatar ענה Michael ב 02 למאי 2014 #

בהצלחה